package fr.upmc.alasca.centreCalcul.interne.machinePhysique;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

import fr.upmc.alasca.centreCalcul.interne.machinePhysique.capteurCoeurs.CapteurCoeurs;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.capteurCoeurs.connectors.CapteurCoeursControleurAbonnementConnector;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.capteurCoeurs.connectors.CapteurCoeursMachinePhysiqueConnector;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.capteurCoeurs.interfaces.CapteurCoeursControleurAbonnementI;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.capteurCoeurs.interfaces.CapteurCoeursMachinePhysiqueI;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.capteurCoeurs.ports.CapteurCoeursControleurAbonnementInboundPort;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.capteurCoeurs.ports.CapteurCoeursControleurAbonnementOutboundPort;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.capteurCoeurs.ports.CapteurCoeursMachinePhysiqueOutboundPort;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.coeur.Coeur;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.coeur.connectors.CoeurOperateurConnector;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.coeur.interfaces.CoeurOperateurI;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.coeur.ports.CoeurOperateurOutboundPort;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.interfaces.MachinePhysiqueAdmissionI;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.interfaces.MachinePhysiqueApplicationI;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.machineVirtuelle.MachineVirtuelle;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.machineVirtuelle.capteurTempsExecution.connectors.CapteurTEMachineVConnector;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.machineVirtuelle.capteurTempsExecution.ports.CapteurTEMachineVInboundPort;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.machineVirtuelle.capteurTempsExecution.ports.CapteurTEMachineVOutboundPort;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.machineVirtuelle.connectors.MachineVirtuelleApplicationConnector;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.machineVirtuelle.interfaces.MachineVirtuelleApplicationI;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.machineVirtuelle.ports.MachineVirtuelleApplicationInboundPort;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.machineVirtuelle.ports.MachineVirtuelleApplicationOutboundPort;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.ports.MachinePhysiqueAdmissionInboundPort;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.ports.MachinePhysiqueApplicationInboundPort;
import fr.upmc.alasca.centreCalcul.objets.CoeurParam;
import fr.upmc.alasca.centreCalcul.objets.Delais;
import fr.upmc.alasca.centreCalcul.objets.EtatCoeur;
import fr.upmc.alasca.centreCalcul.objets.Requete;
import fr.upmc.alasca.centreCalcul.outils.LogMessage;
import fr.upmc.components.AbstractComponent;
import fr.upmc.components.ports.PortI;


/**
 * @author Antoine Lavaire, Sabri Labassi, Thibaut Fleury, Ryme Fantazi
 */
public	class	MachinePhysique
extends			AbstractComponent
implements		MachinePhysiqueAdmissionI, MachinePhysiqueApplicationI
{
	/* ------------------------------------------------------------- ATTRIBUT */
	protected		boolean											isDistributed;
	protected		String 											uriMachinePhysique;
	protected final	float											frequenceMaxCoeurs;
	protected final	float											frequenceMinCoeurs;
	public static final	float										differenceFrequenceCoeurs = (float) 0.5;

	// Permet de stocker les URI des coeurs ainsi que leurs ports d'utilisation
	protected		Map<String, CoeurOperateurOutboundPort>			processeur;

	// Permet de stocker les associations : ApplicationMV <=> Coeurs utilisés
	protected		Map<ApplicationMV, List<String>> 				coeursUtilises;
	// Permet de stocker les associations : uriApplication <=> Coeurs réservés
	protected		Map<String, List<String>> 						coeursReserves;

	// Port permettant d'indiquer au capteur les nouveaux états des coeurs
	protected		CapteurCoeursMachinePhysiqueOutboundPort		capteurCoeur;
	// Port permettant de faire la redirection des appels faits sur le port
	// externe de la MP vers le port du capteurCoeurs (innerComponent)
	protected		CapteurCoeursControleurAbonnementOutboundPort	capteurCoeurAbonnementRedirection;




	/* --------------------------------------------------------- CONSTRUCTEUR */
	public			MachinePhysique
	(
			String	p_uriMachinePhysique,
			boolean	p_isDistributed,
			int		p_nbCoeurs,
			float	p_frequenceMin,
			float	p_frequenceMax
			) throws	Exception
	{
		super(false);

		assert p_nbCoeurs > 0;
		assert p_frequenceMin > 0.0 && p_frequenceMin <= p_frequenceMax;

		// Création des paramètres de la machine physique
		isDistributed = p_isDistributed;
		uriMachinePhysique = p_uriMachinePhysique;
		frequenceMaxCoeurs = p_frequenceMax;
		frequenceMinCoeurs = p_frequenceMin;
		coeursUtilises = new HashMap<ApplicationMV, List<String>>();
		coeursReserves = new HashMap<String, List<String>>();
		
		LogMessage.printMessage("Physique", uriMachinePhysique,
				"Création de la machine physique.");

		// La machine physique offrira des ports proposant le service des machines
		// virtuelles. Ces ports serviront à faire la redirection vers les ports
		// réels des machines virtuelles (composant interne)
		this.addOfferedInterface(MachineVirtuelleApplicationI.class);
		this.addRequiredInterface(MachineVirtuelleApplicationI.class);

		// Création de l'interface du service utilisé par le controleur d'admission
		this.addOfferedInterface(MachinePhysiqueAdmissionI.class);
		PortI portAdmission = new MachinePhysiqueAdmissionInboundPort(
				p_uriMachinePhysique + "_Admission", this);

		// Création de l'interface du service utilisé par le répartiteur de requêtes
		this.addOfferedInterface(MachinePhysiqueApplicationI.class);
		PortI portApplication = new MachinePhysiqueApplicationInboundPort(
				p_uriMachinePhysique + "_Application", this);

		// Publication des deux ports
		this.addPort(portAdmission);
		this.addPort(portApplication);

		if(isDistributed) {
			portAdmission.publishPort();
			portApplication.publishPort();
		}
		else {
			portAdmission.localPublishPort();
			portApplication.localPublishPort();
		}

		// Ajout de l'interface requise pour les opérations sur les coeurs
		this.requiredInterfaces.add(CoeurOperateurI.class);

		creerProcesseur(p_nbCoeurs);

		// Création du capteur à l'écoute des modifications de fréquences et d'état
		// des coeurs du processeur
		creerCapteurCoeur();
	}




	/* -------------------------------------------------------------- METHODE */
	/**
	 * Créé chacun des coeurs dont la machine physique disposera sous forme de 
	 * innerComponents. La machine physique se connecte ensuite à chacun de
	 * ces coeurs via le port "Operateur".
	 * @param p_nbCoeurs Nombre de coeur à allouer à cette machine physique
	 */
	protected	void	creerProcesseur
	(
		int	p_nbCoeurs
	)
	{
		processeur = new HashMap<String, CoeurOperateurOutboundPort>();

		for(int i = 0; i < p_nbCoeurs; i++) {
			String uriCoeur = uriMachinePhysique + "_Coeur_" + i;

			try {
				Coeur tmpCoeur = new Coeur(uriCoeur, frequenceMaxCoeurs, frequenceMinCoeurs);
				this.innerComponents.add(tmpCoeur);

				PortI inboundPort =
						tmpCoeur.findPortsFromInterface(CoeurOperateurI.class)[0];

				CoeurOperateurOutboundPort outboundPort =
						new CoeurOperateurOutboundPort(uriCoeur + "_Outbound", this);

				outboundPort.localPublishPort();

				// Connexion entre la machine physique et le coeur
				outboundPort.doConnection(inboundPort.getPortURI(),
						CoeurOperateurConnector.class.getCanonicalName());

				processeur.put(uriCoeur, outboundPort);
			}
			catch(Exception e) {
				e.printStackTrace();
			}
		}
	}


	/**
	 * Créé le composant capteurCoeur et se connecte à lui pour permettre l'envoi
	 * des évènements se produisant sur les coeurs du processeur.
	 */
	protected 	void 	creerCapteurCoeur
	()
	{
		try {
			CapteurCoeurs capteur = new CapteurCoeurs(uriMachinePhysique);
			this.innerComponents.add(capteur);
			capteur.start();

			// Connexion au port de notification des changements sur les coeurs
			PortI portInbound = capteur.findPortsFromInterface(
					CapteurCoeursMachinePhysiqueI.class)[0];

			capteurCoeur = new CapteurCoeursMachinePhysiqueOutboundPort(
					uriMachinePhysique + "_Capteur_Outbound", this);
			capteurCoeur.localPublishPort();

			capteurCoeur.doConnection(portInbound.getPortURI(), 
					CapteurCoeursMachinePhysiqueConnector.class.getCanonicalName());

			// Connexion au port d'abonnement au service du capteur,
			// pour pouvoir faire la redirection vers le port interne
			portInbound = capteur.findPortsFromInterface(
					CapteurCoeursControleurAbonnementI.class)[0];

			capteurCoeurAbonnementRedirection =
					new CapteurCoeursControleurAbonnementOutboundPort(
							uriMachinePhysique + "_Capteur_Outbound_Redirection",
							this);
			capteurCoeurAbonnementRedirection.localPublishPort();

			capteurCoeurAbonnementRedirection.doConnection(portInbound.getPortURI(), 
					CapteurCoeursControleurAbonnementConnector.class.getCanonicalName());

			// Déploiement du port externe représentant le service d'abonnement
			// du capteur. Les appels faits sur ce port seront redirigés vers
			// le port du capteur.
			PortI capteurCoeurAbonnementExterne =
					new CapteurCoeursControleurAbonnementInboundPort(
							uriMachinePhysique + "_Capteur_Externe", this);
			this.addPort(capteurCoeurAbonnementExterne);

			if(isDistributed)
				capteurCoeurAbonnementExterne.publishPort();
			else
				capteurCoeurAbonnementExterne.localPublishPort();
		}
		catch(Exception e) {
			e.printStackTrace();
		}
	}


	/* -------------------------------------------- MachinePhysiqueAdmissionI */
	@Override
	public 	int 	reserverCoeur
	(
			String 	p_uriApplication, 
			int 	p_nbCoeursAReserver
			) throws 	Exception
	{
		int nbCoeursReserves = 0;

		// Reservation des coeurs si disponibles
		synchronized(processeur) {
			for(String uriCoeur : processeur.keySet()) {
				// Si un coeur est disponible
				if(processeur.get(uriCoeur).getEtat() == EtatCoeur.disponible) {
					
					LogMessage.printMessage("Physique", uriMachinePhysique,
							"Le coeur " + uriCoeur + " étant disponible, "
							+ "il est réservé pour l'application " + p_uriApplication + ".");
					
					processeur.get(uriCoeur).reserverCoeur(p_uriApplication);

					// On ajoute le coeurs réservé
					synchronized(coeursReserves) {
						if(coeursReserves.get(p_uriApplication) == null)
							coeursReserves.put(p_uriApplication, new ArrayList<String>());

						coeursReserves.get(p_uriApplication).add(uriCoeur);
					}

					// On s'arrête quand on a réussi à réserver tous les coeurs
					if(++nbCoeursReserves >= p_nbCoeursAReserver)
						break;
				}
			}
		}
		
		LogMessage.printMessage("Physique", uriMachinePhysique,
				"Coeurs réservés (" + nbCoeursReserves + "/" + p_nbCoeursAReserver + ") "
				+ "pour l'application " + p_uriApplication + ".");

		// Rafraichissement du capteur
		if(nbCoeursReserves > 0)
			notifyCapteur();

		return nbCoeursReserves;
	}


	@Override
	public	void	allouerCoeurs
	(
			String	p_uriApplication
	) throws	Exception
	{	
		synchronized(coeursUtilises) {
			synchronized(coeursReserves) {
				ApplicationMV amv = null;
	
				// L'application a-t-elle deja une MV sur cette MP ?
				for(ApplicationMV amv_tmp : coeursUtilises.keySet()) {
					if (amv_tmp.uriApplication.equals(p_uriApplication)) {
						amv = amv_tmp;
						break;
					}
				}
	
				// Si aucune MV n'existe pour cette application, on en crée nouvelle
				if(amv == null)
					amv = allouerNouvelleMV(p_uriApplication);
				
			
				int cptCoeursAlloues = 0;
				List<String> tmpCoeurs = coeursReserves.get(p_uriApplication);

				if(tmpCoeurs != null && tmpCoeurs.size() > 0) {
					for(String coeur : coeursReserves.get(p_uriApplication)) {
						// Allocation du coeur réservé
						processeur.get(coeur).allouerCoeur(amv.uriMachineVirtuelle);
						
						// Ajout du nouveau coeur dans la liste des coeurs utilisés par cette application
						for(Entry<ApplicationMV, List<String>> mv : coeursUtilises.entrySet()){
							if(mv.getKey().uriApplication.equals(p_uriApplication)) {
								coeursUtilises.get(mv.getKey()).add(coeur);
							}
						}
						
						cptCoeursAlloues++;
					}
					// On retire de la Map tous les coeurs réservés à l'application
					coeursReserves.remove(p_uriApplication);
				}
				
				LogMessage.printMessage("Physique", uriMachinePhysique,
						"Coeurs alloués (" + cptCoeursAlloues + ") pour l'application " + p_uriApplication + ".");
			}
		}

		notifyCapteur();
	}


	@Override
	public	void	relacherCoeur
	(
		String			p_uriApplication,
		List<String>	p_uriCoeurs
	) throws	Exception
	{		
		synchronized (coeursUtilises) {
			synchronized (coeursReserves) {
				// Appel depuis le controleur d'admission
				if (p_uriCoeurs == null) {
					synchronized (processeur) {
						// Pour chaque coeur ...
						for (Entry<String, CoeurOperateurOutboundPort> port_entry : processeur.entrySet()) {
							// Si le coeur appartient à l'application appelante ...
							if (port_entry.getValue().getUriApplication().equals(p_uriApplication)) {
								
								LogMessage.printMessage("Physique", uriMachinePhysique,
										"L'application " + p_uriApplication + 
										" veut relacher le coeur " + port_entry.getKey() + ".");
								
								// On indique au coeur de se désallouer
								port_entry.getValue().desallouerCoeur();

								// On retire le coeur des listes des coeurs réservés
								List<String> listUriCoeursReserves = coeursReserves.get(p_uriApplication);
								if (listUriCoeursReserves != null)
									listUriCoeursReserves.remove(port_entry.getKey());

								// On retire le coeur des listes des coeurs utilisés
								for (ApplicationMV amv : coeursUtilises.keySet()) {
									if (amv.uriApplication.equals(p_uriApplication)) {
										coeursUtilises.get(amv).remove(port_entry.getKey());
										// On éteint la MV si elle n'a plus de coeur
										if (coeursUtilises.get(amv).size() == 0)
											shutdownMV(amv);

										break;
									}
								}
							}
						}
					}
				}
				// Appel depuis le controleur d'application
				else {
					for (String uriCoeurARelacher : p_uriCoeurs) {
						CoeurOperateurOutboundPort port = null;

						synchronized (processeur) {
							if (processeur.containsKey(uriCoeurARelacher))
								port = processeur.get(uriCoeurARelacher);
						}

						// Si le coeur demandé existe bien dans notre processeur
						// (et donc qu'on a réussi à récupérer son port de communication)
						if (port != null && !port.getEtat().equals(EtatCoeur.disponible)) {
							// On indique au coeur de se désallouer
							port.desallouerCoeur();
							
							LogMessage.printMessage("Physique", uriMachinePhysique,
									"L'application " + p_uriApplication + 
									" veut relacher le coeur " + uriCoeurARelacher + ".");

							// On retire le coeur des listes des coeurs réservés
							List<String> listUriCoeursReserves = coeursReserves.get(p_uriApplication);
							if (listUriCoeursReserves != null)
								listUriCoeursReserves.remove(uriCoeurARelacher);

							// On retire le coeur des listes des coeurs utilisés
							for (ApplicationMV amv : coeursUtilises.keySet()) {
								if (amv.uriApplication.equals(p_uriApplication)) {
									coeursUtilises.get(amv).remove(uriCoeurARelacher);
									// On éteint la MV si elle n'a plus de coeur
									if (coeursUtilises.get(amv).size() == 0)
										shutdownMV(amv);

									break;
								}
							}
						}
					}
				}
			}
		}

		notifyCapteur();
	}
	/* -------------------------------------------- MachinePhysiqueAdmissionI */


	/* ------------------------------------------ MachinePhysiqueApplicationI */
	@Override
	public	String 	recupererMV
	(
			String	p_uriApplication
			) throws	Exception
	{
		String uriMV = null;

		// Rattache les coeurs réservés par l'application à la MV de celle-ci.
		// Si aucune MV n'existe encore pour cette application, on la créé.
		allouerCoeurs(p_uriApplication);

		synchronized(coeursUtilises) {
			for(ApplicationMV amv : coeursUtilises.keySet()) {
				// Si on trouve une MV associée, on renvoie l'URI du port externe
				if(amv.uriApplication.equals(p_uriApplication)) {
					uriMV = amv.uriMachineVirtuelle;
					break;
				}
			}
		}

		return uriMV;
	}


	@Override
	public	void	shutdownApplication
	(
			String	p_uriApplication
			) throws	Exception
	{
		synchronized(coeursUtilises) {
			for(ApplicationMV amv : coeursUtilises.keySet()) {
				// On récupère la liste des uriCoeurs associées à l'uri de l'application
				if(amv.uriApplication.equals(p_uriApplication)) {
					// Pour chacun d'entre eux ...
					for(String uriCoeur : coeursUtilises.get(amv)) {
						// On les désalloue afin qu'ils se "décrochent" des MV
						synchronized(processeur) {
							CoeurOperateurOutboundPort port = processeur.get(uriCoeur);
							
							port.desallouerCoeur();
						}
					}
					
					LogMessage.printMessage("Physique", uriMachinePhysique,
							"Arrêt de l'application " + p_uriApplication + ".");

					shutdownMV(amv);
					break;
				}
			}
		}

		notifyCapteur();
	}


	@Override
	public	boolean	reserverNouvelleFrequence
	(
			String	p_uriApplication,
			String	p_uriCoeur,
			float	p_nouvelleFrequence
			) throws	Exception
	{
		// Si la nouvelle fréquence ne respècte pas les contraintes des coeurs
		if(p_nouvelleFrequence < frequenceMinCoeurs || frequenceMaxCoeurs < p_nouvelleFrequence)
			return false;

		synchronized(processeur) {
			for(CoeurOperateurOutboundPort coeur : processeur.values()) {
				if(coeur.getEtat() == EtatCoeur.utilise || coeur.getEtat() == EtatCoeur.reserve) {
					// On doit vérifier s'il a une fréquence réservée
					if(coeur.aReservationFrequenceEnCours()) {
						if(coeur.getFrequenceReservee() - p_nouvelleFrequence > differenceFrequenceCoeurs
								|| p_nouvelleFrequence - coeur.getFrequenceReservee() > differenceFrequenceCoeurs)
							return false;
					}
					else {
						if(coeur.getFrequence() - p_nouvelleFrequence > differenceFrequenceCoeurs
								|| p_nouvelleFrequence - coeur.getFrequence() > differenceFrequenceCoeurs) 
							return false;
					}
				}
			}

			processeur.get(p_uriCoeur).reserverFrequence(p_nouvelleFrequence);
		}

		notifyCapteur();

		return true;
	}


	@Override
	public	float	appliquerNouvelleFrequence
	(
			String	p_uriApplication
			) throws	Exception
	{
		float nouvelleFrequence = 0;

		synchronized(processeur) {
			synchronized (coeursUtilises) {
				ApplicationMV appMV = null;

				for (ApplicationMV tmp_appMV : coeursUtilises.keySet()) {
					if (tmp_appMV.uriApplication.equals(p_uriApplication)) {
						appMV = tmp_appMV;
						break;
					}
				}

				for(String uriCoeur : coeursUtilises.get(appMV)) {
					CoeurOperateurOutboundPort coeur = processeur.get(uriCoeur);
					if(coeur.aReservationFrequenceEnCours()){
						coeur.changerFrequence();
						nouvelleFrequence = coeur.getFrequence();
					}

				}
			}
		}


		notifyCapteur();

		return nouvelleFrequence;
	}
	/* ------------------------------------------ MachinePhysiqueApplicationI */


	/**
	 * Alloue une nouvelle Machine Virtuelle attribuée à l'application appelante.
	 * Créé également un port inbound extérieur à la Machine Physique pour 
	 * permettre les appels.
	 * @param p_uriApplication Identifiant de l'application appelante
	 * @return Informations concernant la Machine Virtuelle créée
	 */
	protected	ApplicationMV	allouerNouvelleMV
	(
		String	p_uriApplication
	) throws Exception
	{
		try {
			String uriMachineVirtuelle = uriMachinePhysique + "_MV_" + p_uriApplication;
			MachineVirtuelle mv = new MachineVirtuelle(
					uriMachineVirtuelle, new Delais(2, TimeUnit.SECONDS));
			mv.start();

			PortI portInbound =
					mv.findPortsFromInterface(MachineVirtuelleApplicationI.class)[0];

			MachineVirtuelleApplicationOutboundPort portOutbound =
					new MachineVirtuelleApplicationOutboundPort(
							uriMachineVirtuelle + "_Application_Outbound", this);
			portOutbound.localPublishPort();

			portOutbound.doConnection(portInbound.getPortURI(),
					MachineVirtuelleApplicationConnector.class.getCanonicalName());

			// Création d'un port de la machine physique proposant les services
			// de la machine virtuelle qui vient d'être créée
			PortI portMVApplication = 
					new MachineVirtuelleApplicationInboundPort(
							uriMachineVirtuelle + "_Externe", this);
			this.addPort(portMVApplication);

			if(isDistributed)
				portMVApplication.publishPort();
			else
				portMVApplication.localPublishPort();

			// Création d'un port
			CapteurTEMachineVOutboundPort capteurOutboundPort = 
					new CapteurTEMachineVOutboundPort(
							uriMachineVirtuelle + "_CapteurTE_Outbound", this);
			capteurOutboundPort.localPublishPort();

			capteurOutboundPort.doConnection(uriMachineVirtuelle + "_CapteurTempsExecution",
					CapteurTEMachineVConnector.class.getCanonicalName());

			// Création d'un port de la machine physique proposant les services
			// du capteurTE qui est un composant interne la la machine virtuelle
			PortI portCapteur = 
					new CapteurTEMachineVInboundPort(
							uriMachineVirtuelle + "_CapteurTE_Externe", this);
			this.addPort(portCapteur);

			if(isDistributed)
				portCapteur.publishPort();
			else
				portCapteur.localPublishPort();

			// Toutes les informations sont empaquetées
			ApplicationMV amv = new ApplicationMV(
					p_uriApplication,
					uriMachineVirtuelle,
					mv,
					portOutbound,
					capteurOutboundPort);

			coeursUtilises.put(amv, new ArrayList<String>());

			return amv;
		}
		catch(Exception e) {
			e.printStackTrace();
		}

		return null;
	}




	public	void				soumettreRequete
	(
		String	p_uriPortAppelant,
		Requete	p_requete
	) throws Exception
	{
		synchronized(coeursUtilises) {
			for(ApplicationMV amv : coeursUtilises.keySet()) {
				if(p_uriPortAppelant.equals(amv.uriMachineVirtuelle + "_Externe"))
					amv.portMV.soumettreRequete(p_requete);
			}
		}
	}


	/**
	 * Méthode de redirection de l'appel externe vers le port du capteurCoeurs.
	 * @param p_uriCallback
	 * @throws Exception
	 */
	public	void	abonner
	(
		String	p_uriCallback
	) throws	Exception
	{
		capteurCoeurAbonnementRedirection.abonner(p_uriCallback);
	}


	/**
	 * Méthode de redirection de l'appel externe vers le port du capteurCoeurs.
	 * @param p_uriControleurApplication
	 * @param p_uriPortAppelant
	 * @throws Exception
	 */
	public	void	abonnementCapteurTE
	(
		String	p_uriControleurApplication,
		String	p_uriPortAppelant
	) throws	Exception
	{
		synchronized(coeursUtilises) {
			for(ApplicationMV amv : coeursUtilises.keySet()) {
				if(p_uriPortAppelant.equals(amv.uriMachineVirtuelle + "_CapteurTE_Externe"))
					amv.capteurOutboundPort.abonnementCapteurTE(p_uriControleurApplication);
			}
		}
	}



	private	void	shutdownMV
	(
		ApplicationMV	p_amv
	) throws	Exception
	{
		p_amv.capteurOutboundPort.doDisconnection();
		p_amv.portMV.doDisconnection();

		p_amv.mv.shutdown();
		coeursUtilises.remove(p_amv);
	}


	/**
	 * Récupère les informations concernant tous les coeurs de la machine 
	 * physique et les envoie à tous les controleurs abonnés au capteur.
	 */
	private void	notifyCapteur
	()
	{
		List<CoeurParam> params = new ArrayList<>();

		synchronized(processeur) {
			for(String uriCoeur : processeur.keySet()) {
				EtatCoeur etat = null;
				float frequence = 0;
				String uriApplication = null;

				try {
					etat = processeur.get(uriCoeur).getEtat();
					uriApplication = processeur.get(uriCoeur).getUriApplication();

					if (processeur.get(uriCoeur).aReservationFrequenceEnCours())
						frequence = processeur.get(uriCoeur).getFrequenceReservee();
					else
						frequence = processeur.get(uriCoeur).getFrequence();
				}
				catch(Exception e) {
					e.printStackTrace();
				}

				params.add(new CoeurParam(uriCoeur, etat, frequence, uriApplication));
			}
		}

		try {
			capteurCoeur.modificationCoeur(params);
		}
		catch(Exception e) {
			e.printStackTrace();
		}
	}



	/* ------------------------------------------------------- CLASSE INTERNE */
	// Liaison entre l'identifiant d'une application, l'URI de 
	// la machine virtuelle associée, le composant Machine Virtuelle,
	// le port Outbound de cette machine virtuelle permettant de rediriger
	// les appels et le capteur de la machine virtuelle.
	private class ApplicationMV
	{
		public String 									uriApplication;
		public String 									uriMachineVirtuelle;
		public MachineVirtuelle 						mv;
		public MachineVirtuelleApplicationOutboundPort 	portMV;
		public CapteurTEMachineVOutboundPort			capteurOutboundPort;

		public ApplicationMV
		(
				String									p_uriApplication,
				String									p_uriMachineVirtuelle,
				MachineVirtuelle						p_mv,
				MachineVirtuelleApplicationOutboundPort	p_portMV,
				CapteurTEMachineVOutboundPort			p_capteurOutboundPort
				)
		{
			uriApplication 			= p_uriApplication;
			uriMachineVirtuelle 	= p_uriMachineVirtuelle;
			mv 						= p_mv;
			portMV					= p_portMV;
			capteurOutboundPort		= p_capteurOutboundPort;
		}
	}

}
